package tcln.sors.client.core.mvc;

import java.util.Map;

import tcln.commons.assertion.Assert;
import tcln.commons.collections.CollectionUtils;
import tcln.commons.lang.BooleanUtils;

/**
 * The default implementation for an {@link ApplicationController} which provides a single point to register and lookup
 * for controllers.
 * <p>
 * On application startup, it's necessary to register controller types with classes, one by one. Method
 * {@link #register(String, Class)} - or one of its overloads - is used for this purpose. Duplication is allowed, which
 * means that these mappings can be changed at runtime.
 * 
 * @author Truong Chau Lien Ngoc (lienngoc@gmail.com)
 */
public final class SingleApplicationController extends AbstractApplicationController {
    private static volatile SingleApplicationController instance;

    private Map<String, Class<? extends Controller<?>>> controllerClassMap = CollectionUtils.newHashMap();
    private Map<String, Class<? extends View<?>>> viewClassMap = CollectionUtils.newHashMap();
    private Map<String, Boolean> singletonAttributeMap = CollectionUtils.newHashMap();

    /**
     * Returns the single instance of this controller.
     * 
     * @return
     */
    public static SingleApplicationController getInstance() {
        if (instance == null) {
            synchronized (SingleApplicationController.class) {
                instance = new SingleApplicationController();
            }
        }
        return instance;
    }

    private SingleApplicationController() {
        // to hide
    }

    /**
     * Registers a controller type with a controller class.
     * 
     * @param controllerType
     * @param controllerClass
     * @return
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SingleApplicationController register(String controllerType, Class<? extends Controller<?>> controllerClass) {
        return register(controllerType, controllerClass, (Class) DefaultView.class);
    }

    /**
     * Registers a controller type with a controller class as <i>NON-SINGLETON</i>.
     * 
     * @param controllerType
     * @param controllerClass
     * @param viewClass
     * @return
     */
    public SingleApplicationController register(String controllerType, Class<? extends Controller<?>> controllerClass,
            Class<? extends View<?>> viewClass) {
        return register(controllerType, controllerClass, viewClass, false);
    }

    /**
     * Registers a controller type with a controller class.
     * 
     * @param controllerType
     * @param controllerClass
     * @param viewClass
     * @param singleton
     *            <code>true</code> to register given controller and view as 'singleton'.
     * @return
     */
    public SingleApplicationController register(String controllerType, Class<? extends Controller<?>> controllerClass,
            Class<? extends View<?>> viewClass, boolean singleton) {
        Assert.ARGUMENTS.notEmpty(controllerType, "Controller type is missing");
        Assert.ARGUMENTS.notNull(controllerClass, "Controller class is missing");
        Assert.ARGUMENTS.notNull(viewClass, "View class is missing");

        controllerClassMap.put(controllerType, controllerClass);
        viewClassMap.put(controllerType, viewClass);
        singletonAttributeMap.put(controllerType, singleton);

        logger.info("Registered controller type \"{0}\" (singleton={3}): {1} -> {2}.", controllerType, controllerClass,
                viewClass, singleton);

        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Class<?> lookupControllerClass(String type) {
        return this.controllerClassMap.get(type);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected Class<?> lookupViewClass(String type, Map<String, Object> parameters) {
        return this.viewClassMap.get(type);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean isSingletonController(String type) {
        return BooleanUtils.isTrue(singletonAttributeMap.get(type));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean isSingletonView(String type) {
        return isSingletonController(type);
    }

}
